<?php
namespace PHPSpring\Annotation\Impl;

use PHPSpring\Core\RuntimeAnnotation;
use PHPSpring\Utils\Utils;
use PHPSpring\Core\BizException;

class Validation {

  const DEFAULT_ARG_KEY = 'default';
  const GROUPS_ARG_KEY = 'groups';
  const MESSAGE_ARG_KEY = 'message';

  public static function before ($annoArgs, $ret, $target, $method, $args) {
    $targetAnnos = RuntimeAnnotation::getAnnotations(get_class($target));
    if (empty($targetAnnos) || !isset($targetAnnos['methods'])
        || !isset($targetAnnos['methods'][$method])
        || empty($targetAnnos['methods'][$method]['params'])) {
      return $ret;
    }
    $params = $targetAnnos['methods'][$method]['params'];
    $len = count($params);
    $argsLen = count($args);
    $paramsError = array();
    for ($i=0; $i < $len; $i++) {
      $param = $params[$i];
      $value = $param['default'];
      if ($i < $argsLen) {
        $value = $args[$i];
      }

      $errors = self::validate($target, $method, $param['type'], $value);
      if (empty($errors)) {
        continue;
      }
      $paramsError[$param['name']] = $errors;
    }
    if (!empty($paramsError)) {
      $ept = new BizException("Validation Error");
      $ept->setData($paramsError);
      throw $ept;
    }
    return $ret;
  }

  private static function validate ($target, $method, $paramClz, $paramVal) {
    if (Utils::isPrimaryType($paramClz)) {
      return;
    }
    $validators = self::getValidators($paramClz);
    if (empty($validators)) {
      return;
    }

    if (empty($paramVal)) {
      throw new BizException("参数不能为空");
    }

    $initedProperties = array();
    foreach ($paramVal as $k => $v) {
      $initedProperties[$k] = $v;
    }
    $propertiesMessages = array();
    foreach ($validators as $propertyName => $propertyInfo) {
      $propertyType = $propertyInfo['type'];
      $thisValidators = $propertyInfo['validators'];
      $propertyValue = $initedProperties[$propertyName] ?? null;
      $messages = array();
      foreach ($thisValidators as $validator) {
        list($validatorType, $validatorArgs) = $validator;
        $message = $validatorType::validate($validatorArgs, $propertyName, $propertyType, $propertyValue);
        if (!empty($message)) {
          $messages[] = $message;
        }
      }
      if (empty($messages)) {
        continue;
      }
      $propertiesMessages[$propertyName] = $messages;
    }
    return empty($propertiesMessages) ? null : $propertiesMessages;
  }

  public static function getValidators ($paramClz) {
    $paramAnnos = RuntimeAnnotation::getAnnotations($paramClz);
    if (empty($paramAnnos['class']) || empty($paramAnnos['properties'])) {
      return;
    }
    $allValidators = array();
    foreach ($paramAnnos['properties'] as $property) {
      $validators = array();
      foreach ($property['annos'] as $validatorName => $anno) {
        $type = $anno[0];
        $implements = class_implements($type);
        if (!isset($implements['PHPSpring\Annotation\IValidator'])) {
          continue;
        }
        $annoArgs = $anno[1];
        $annoArgMap = array();
        foreach ($annoArgs as $argStr) {
          list($k, $v) = self::argStr2Map($argStr);
          $annoArgMap[$k] = $v;
        }
        if (!empty($annoArgMap)) {
          self::resetGroupArgs($annoArgMap);
        }

        $validators[] = array($type, $annoArgMap);
      }
      if (empty($validators)) {
        continue;
      }
      $allValidators[$property['name']] = array(
        'type' => $property['type'], // TODO: 复杂类型支持
        'validators' => $validators
      );
    }
    return $allValidators;
  }

  private static function argStr2Map ($argStr) {
    $argParts = explode('=', $argStr, 2);
    $defaultArg = null;
    $key = self::DEFAULT_ARG_KEY;
    $val = $argStr;
    $keyExist = false;
    if (count($argParts) == 2) {
      $argParts[0] = trim($argParts[0]);
      $argParts[1] = trim($argParts[1]);
      if (preg_match('/^[_\w]([_\d\w])*$/', $argParts[0])) {
        $key = $argParts[0];
        $val = $argParts[1];
        $keyExist = true;
      }
    }

    if ($keyExist) {
      $val = eval('return ' . $val . ';');
    }
    return array($key, $val);
  }

  private static function resetGroupArgs (array &$args) {
    $groups = isset($args[self::GROUPS_ARG_KEY]) ? $args[self::GROUPS_ARG_KEY] : array();
    if (is_string($groups)) {
      $groups = trim($groups);
      if (!empty($groups)) {
        $groups = array($groups);
      } else {
        $groups = array();
      }
    }
    $args[self::GROUPS_ARG_KEY] = $groups;
  }

  public static function resetMessageArgs (array &$args, $default) {
    $message = isset($args[self::MESSAGE_ARG_KEY]) ? $args[self::MESSAGE_ARG_KEY] :
                     (isset($args[self::DEFAULT_ARG_KEY]) ? $args[self::DEFAULT_ARG_KEY] : $default);
    $args[self::MESSAGE_ARG_KEY] = $message;
  }

  public static function skipValidateByGroup ($group, $args) {
    if (!empty($group) && !empty($args[self::GROUPS_ARG_KEY])
        && !in_array($group, $args[self::GROUPS_ARG_KEY])) {
      return true;
    }
  }

}
